﻿#define USE_TESTCONSOLE  
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

namespace RuntimeConsole
{
    struct Log
    {
        public string message;
        public string stackTrace;
        public LogType type;
    }
    /// <summary>  
    /// A console to display Unity's debug logs in-game.  
    /// </summary>  
    class RuntimeConsole : MonoBehaviour
    {
#if USE_TESTCONSOLE
        public bool DefaultVisible ;

        #region Inspector Settings  

        /// <summary>  
        /// The hotkey to show and hide the console window.  
        /// </summary>  
        public KeyCode toggleKey = KeyCode.BackQuote;

        /// <summary>  
        /// Whether to open the window by shaking the device (mobile-only).  
        /// </summary>  
        public bool shakeToOpen = true;

        /// <summary>  
        /// The (squared) acceleration above which the window should open.  
        /// </summary>  
        public float shakeAcceleration = 3f;

        /// <summary>  
        /// Whether to only keep a certain number of logs.  
        ///  
        /// Setting this can be helpful if memory usage is a concern.  
        /// </summary>  
        public bool restrictLogCount = false;

        /// <summary>  
        /// Number of logs to keep before removing old ones.  
        /// </summary>  
        public int maxLogs = 1000;

        #endregion

        public List<Log> logs = new List<Log>();
        public List<Log> filterLogs = new List<Log>();
        Vector2 scrollPosition;

        private bool _visible = false;
        bool Visible
        {
            get { return _visible; }
            set
            {
                _visible = value;
                if (value)
                {
                    Showing = _showing;
                }else
                {
                    SetGoActive(controlPanel, false);
                    SetGoActive(floatButton, false);
                }
            }
        }
        bool _showing;
        public bool Showing
        {
            get { return _showing; }
            set {
                _showing = value;
                if (Visible)
                {
                    SetGoActive(controlPanel, value);
                    SetGoActive(floatButton, !value);
                }
            }
        }
        private void SetGoActive (GameObject go,  bool value)
        {
            if (go)
            {
                go.SetActive(value);
            }
        }

        // Visual elements:  

        static readonly Dictionary<LogType, Color> logTypeColors = new Dictionary<LogType, Color>
        {
            { LogType.Assert, Color.white },
            { LogType.Error, Color.red },
            { LogType.Exception, Color.red },
            { LogType.Log, Color.white },
            { LogType.Warning, Color.yellow },
        };

        const string windowTitle = "Console";
        const int margin = 20;
        static readonly GUIContent clearLabel = new GUIContent("Clear", "Clear the contents of the console.");
        static readonly GUIContent collapseLabel = new GUIContent("Collapse", "Hide repeated messages.");

        readonly Rect titleBarRect = new Rect(0, 0, 10000, 20);
        Rect windowRect;

        public GameObject floatButton;
        public GameObject controlPanel;
        Regex filterReg;

        void Awake()
        {
            DontDestroyOnLoad(transform.gameObject);
        }

        private void Start()
        {
            Rect t = controlPanel.GetComponent<RectTransform>().rect;
            windowRect = new Rect(margin, margin, Screen.width - (margin * 2), Screen.height - t.height - margin);
            Showing = false;
            Visible = DefaultVisible;
        }

        void OnEnable()
        {
#if UNITY_5
            Application.logMessageReceived += HandleLog;
#else
            Application.RegisterLogCallback(HandleLog);  
#endif
        }

        void OnDisable()
        {
#if UNITY_5
            Application.logMessageReceived -= HandleLog;
#else
            Application.RegisterLogCallback(null);  
#endif
        }

        void Update()
        {
            if (Input.GetKeyDown(toggleKey))
            {
                Debug.Log(toggleKey);
                Visible = !Visible;
            }
            if (shakeToOpen && Input.acceleration.sqrMagnitude > shakeAcceleration)
            {
                Showing = true;
                Visible = true;
            }
        }

        void OnGUI()
        {
            if (!Showing || !Visible)
            {
                return;
            }
            windowRect = GUILayout.Window(123456, windowRect, DrawConsoleWindow, windowTitle);
        }

        /// <summary>  
        /// Displays a window that lists the recorded logs.  
        /// </summary>  
        /// <param name="windowID">Window ID.</param>  
        void DrawConsoleWindow(int windowID)
        {
            DrawLogsList();
            //DrawToolbar();  

            // Allow the window to be dragged by its title bar.  
            GUI.DragWindow(titleBarRect);
        }

        /// <summary>  
        /// Displays a scrollable list of logs.  
        /// </summary>  
        void DrawLogsList()
        {
            scrollPosition = GUILayout.BeginScrollView(scrollPosition);

            List<Log> list = filterReg == null ? logs : filterLogs; 

            // Iterate through the recorded logs.  
            for (var i = 0; i < list.Count; i++)
            {
                var log = list[i];
                GUI.contentColor = logTypeColors[log.type];
                GUILayout.Label(log.message);
            }

            GUILayout.EndScrollView();

            // Ensure GUI colour is reset before drawing other components.  
            GUI.contentColor = Color.white;
        }

        /// <summary>  
        /// Records a log from the log callback.  
        /// </summary>  
        /// <param name="message">Message.</param>  
        /// <param name="stackTrace">Trace of where the message came from.</param>  
        /// <param name="type">Type of message (error, exception, warning, assert).</param>  
        void HandleLog(string message, string stackTrace, LogType type)
        {
            Log log = new Log
            {
                message = message,
                stackTrace = stackTrace,
                type = type,
            };

            logs.Add(log);
            if (filterReg != null && filterReg.IsMatch(message))
            {
                filterLogs.Add(log);
            }

            if (restrictLogCount)
            {
                TrimExcessLogs(logs);
                TrimExcessLogs(filterLogs);
            }
        }

        /// <summary>  
        /// Removes old logs that exceed the maximum number allowed.  
        /// </summary>  
        void TrimExcessLogs(List<Log> list)
        {
            var amountToRemove = Mathf.Max(list.Count - maxLogs, 0);
            if (amountToRemove == 0)
            {
                return;
            }
            list.RemoveRange(0, amountToRemove);
        }

        public void SetFilter(string reg = "")
        {
            if (reg == null || reg == "")
            {
                filterReg = null;
                filterLogs.Clear();
            }
            else
            {
                filterReg = new Regex(reg);

                for (var i = 0; i < logs.Count; i++)
                {
                    var log = logs[i];

                    if (filterReg.IsMatch(log.message))
                    {
                        filterLogs.Add(log);
                    }
                }
            }
        }
#endif
    }
}